// GetBackupFile.cpp : Defines the entry point for the console application.
// Using Visual Studio 2013 community eddition with 'Casablanca' (REST/JSON module) 
// and also XGetopt for C++ from Hans Dietrich <hdietrich2@hotmail.com>, can't help my unix self.
//
//  HP OneView API Reference
//  http://h17007.www1.hp.com/docs/enterprise/servers/oneviewhelp/oneviewRESTAPI/content/images/api/index.html
//

#include "stdafx.h"
#include "cpprest/containerstream.h"
#include "cpprest/filestream.h"
#include "cpprest/http_client.h"
#include "cpprest/json.h"
#include "cpprest/producerconsumerstream.h"
#include <iostream>
#include <sstream>
#include <windows.h>
#include "XGetopt.h"

using namespace ::pplx;
using namespace utility;
using namespace concurrency::streams;

using namespace web::http;
using namespace web::http::client;
using namespace web::json;

void PrintUsage(void)
{
	std::wcerr << L"Usage: GetBakupFile [ -s HPOneView ] [ -d HQ ] -u Username -p Password" << std::endl;
	std::wcerr << L"Creates a new backup and downloads it from HP Oneview appiance" << std::endl;
	std::wcerr << L"placing it into the current working directory" << std::endl;
	std::wcerr << L"" << std::endl;
	std::wcerr << L"	-s Oneview hostname" << std::endl;
	std::wcerr << L"	-d Login Domain" << std::endl;
	std::wcerr << L"	-u User Name" << std::endl;
	std::wcerr << L"	-p Password" << std::endl;
	std::wcerr << L"" << std::endl;
	exit(-1);
}

void 
OVLogin(utility::string_t &sessionID, utility::string_t &server, 
	utility::string_t  &domain, utility::string_t  &uname, utility::string_t  &passwd)
{
	std::wcout << L"OVLogin called..." << std::endl; 
	web::json::value postData;
	postData[L"authLoginDomain"] = web::json::value::string(domain,true);
	postData[L"password"] = web::json::value::string(passwd,true);
	postData[L"userName"] = web::json::value::string(uname,true);
	
	utility::string_t Server = U("https://") + server;
	http_client client(Server);
	http_request request(methods::POST);
	request.set_request_uri(L"/rest/login-sessions");
	request.headers().set_content_type(L"application/json");
	request.set_body(postData);

	// std::wcout << request.to_string() << std::endl << std::endl;
	// Make request and wait for entire body to arrive.
	http_response response = client.request(request).get();
	response.content_ready().wait();
	//std::wcout << response.to_string() << std::endl << std::endl;
	std::wcout << L"Response code: " << response.status_code() << L": " << response.reason_phrase() << std::endl;

	if (response.status_code() != status_codes::OK)
		return;
	
	try 
	{
		web::json::value& obj = response.extract_json().get();
	
		if (obj.has_field(U("sessionID")))
			sessionID = obj.at(U("sessionID")).as_string().c_str();
	
	}
	catch (json_exception const & e)
	{
			std::wcout << e.what() << std::endl;
	}

}

void
OVLogout(utility::string_t &sessionID, utility::string_t &server)
{
	std::wcout << L"OVLogout called..." << std::endl;
	utility::string_t Server = U("https://") + server;
	http_client client(Server);
	http_request request(methods::DEL);
	request.set_request_uri(L"/rest/login-sessions");
	request.headers().set_content_type(L"application/json");
	request.headers().add(U("Auth"), sessionID.c_str() );

	// std::wcout << request.to_string() << std::endl << std::endl;
	// Make request and wait for entire body to arrive.
	http_response response = client.request(request).get();
	response.content_ready().wait();
	//std::wcout << response.to_string() << std::endl << std::endl;
	std::wcout << L"Response code: " << response.status_code() << L": " << response.reason_phrase() << std::endl;
	
	if (response.status_code() != status_codes::NoContent)
		return;

	sessionID = L"";
	
}

void
OVStartBackup(utility::string_t &sessionID, utility::string_t &server, utility::string_t &associatedResourceUri)
{
	std::wcout << L"OVStartBackup called..." << std::endl;

	utility::string_t Server = U("https://") + server;
	http_client client(Server);
	http_request request(methods::POST);
	request.set_request_uri(L"/rest/backups");
	request.headers().set_content_type(L"application/json");
	request.headers().add(U("Auth"), sessionID.c_str());

	// std::wcout << request.to_string() << std::endl << std::endl;
	// Make request and wait for entire body to arrive.
	http_response response = client.request(request).get();
	response.content_ready().wait();
	//std::wcout << response.to_string() << std::endl << std::endl;
	std::wcout << L"Response code: " << response.status_code() << L": " << response.reason_phrase() << std::endl;
	
	// If the status is OK extract the body of the response into a JSON value
	if (response.status_code() != status_codes::Accepted)
		return;

	try
	{
		web::json::value& obj = response.extract_json().get();

		if (obj.has_field(U("message")))
			std::wcout << obj.at(U("message")).as_string().c_str() << std::endl;

		if (obj.has_field(U("associatedResourceUri")))
			associatedResourceUri = obj.at(U("associatedResourceUri")).as_string().c_str();

	}
	catch (json_exception const & e)
	{
		std::wcout << e.what() << std::endl;
	}
}

void
OVBackupStatus(utility::string_t &sessionID, utility::string_t &server,
	utility::string_t &associatedResourceUri, utility::string_t &status, utility::string_t &downloadUri)
{
	std::wcout << L"OVBackupStatus called..." << std::endl;

	utility::string_t Server = U("https://") + server;
	http_client client(Server);
	http_request request(methods::GET);
	request.set_request_uri( associatedResourceUri.c_str() );
	request.headers().set_content_type(L"application/json");
	request.headers().add(U("Auth"), sessionID.c_str());

	// std::wcout << request.to_string() << std::endl << std::endl;
	// Make request and wait for entire body to arrive.
	http_response response = client.request(request).get();
	response.content_ready().wait();
	// std::wcout << response.to_string() << std::endl << std::endl;
	std::wcout << L"Response code: " << response.status_code() << L": " << response.reason_phrase() << std::endl;
	
	if (response.status_code() != status_codes::OK)
		return;

	try
	{
		web::json::value& obj = response.extract_json().get();

		if (obj.has_field(U("percentComplete")))
			std::wcout << L"Percent Complete: " << obj.at(U("percentComplete")) << std::endl;

		if (obj.has_field(U("status")))
			status = obj.at(U("status")).as_string().c_str();

		if (obj.has_field(U("downloadUri")))
			downloadUri = obj.at(U("downloadUri")).as_string().c_str();

	}
	catch (json_exception const & e)
	{
		std::wcout << e.what() << std::endl;
	}
}

void 
OVDownloadBackup(utility::string_t &sessionID, utility::string_t &server, utility::string_t &downloadUri)
{
	std::wcout << L"OVDownloadBackup called..." << std::endl;

	utility::string_t filename = downloadUri + U(".bkp"); // why is this missing?
	const size_t last_slash_idx = filename.find_last_of(L"/");
	if (utility::string_t::npos != last_slash_idx)
		filename.erase(0, last_slash_idx + 1);
	std::wcout << L" Writing file: " << filename << std::endl;

	utility::string_t Server = U("https://") + server;
	http_client client(Server);
	http_request request(methods::GET);
	request.set_request_uri(downloadUri.c_str());
	request.headers().set_content_type(L"'application/octetstream;q=0.8,application/json'");
	request.headers().add(U("Auth"), sessionID.c_str());

	// std::wcout << request.to_string() << std::endl << std::endl;
	// Make request and wait for entire body to arrive.
	http_response response = client.request(request).get();
	response.content_ready().wait();
	//std::wcout << response.to_string() << std::endl << std::endl;
	std::wcout << L"Response code: " << response.status_code() << L": " << response.reason_phrase() << std::endl;

	if (response.status_code() != status_codes::OK)
	{
		std::wcerr << L"Error! Did not recieve any data for " << filename << std::endl;
		return;
	}

	auto filestream = concurrency::streams::file_stream<uint8_t>::open_ostream(filename).get();
	request.set_response_stream(filestream);
	filestream.close().wait();

	std::wcout << L" Done, Writing file: " << filename << std::endl;

}

int _tmain(int argc, _TCHAR* argv[])
{
	utility::string_t server = U("hponeview"); 
	utility::string_t domain = U("HQ");
	utility::string_t uname  = U("");
	utility::string_t passwd = U("");

	int c;
	while ( (c = getopt(argc, argv, _T("s:d:u:p:"))) != EOF )
	{
		switch (c)
		{
		case _T('s'):
			server = utility::conversions::to_string_t(optarg);
			break;
		case _T('d'):
			domain = utility::conversions::to_string_t(optarg);
			break;
		case _T('u'):
			uname = utility::conversions::to_string_t(optarg);
			break;
		case _T('p'):
			passwd = utility::conversions::to_string_t(optarg);
			break;
		case _T('?'):
		case _T('h'):
		default:
			PrintUsage();
		}
	}

	if (server == L"" || domain == L"" || uname == L"" || passwd == L"")
		PrintUsage();

	utility::string_t sessionID( L"" );
	OVLogin( sessionID, server, domain, uname, passwd );
	if ( sessionID.empty() ) {
		std::wcout << L"Can't login, good bye!" << std::endl;
		return -1;
	}
	//Debug std::wcout << L"SessionID is: '" << sessionID.c_str() << L"'" << std::endl;

	utility::string_t associatedResourceUri(L"");
	OVStartBackup( sessionID, server, associatedResourceUri );
	if (associatedResourceUri.empty()) {
		OVLogout(sessionID, server);
		std::wcout << L"Can't start new backup, good bye!" << std::endl;
		return -1;
	}
	//std::wcout << L"associatedResourceUri is: '" << associatedResourceUri.c_str() << L"'" << std::endl;

	utility::string_t downloadUri(L"");
	utility::string_t status(L"");
	do
	{	
		std::wcout << L"Waiting..." << std::endl;
		Sleep(5000); // 5 second sleep
		OVBackupStatus(sessionID, server, associatedResourceUri, status, downloadUri);
	} while (status == U("IN_PROGRESS"));
	//std::wcout << L"downloadUri is: '" << downloadUri.c_str() << L"'" << std::endl;

	OVDownloadBackup(sessionID, server, downloadUri);

	OVLogout( sessionID, server );
	std::wcout << L"Good bye!" << std::endl;

	return 0;
}